<?php

namespace common\libraries;


/**
 *
 */
class HTTPClient
{

    const METHOD_PUT = 'PUT';
    const METHOD_POST = 'POST';
    const METHOD_GET = 'GET';
    const METHOD_HEAD = 'HEAD';
    const METHOD_DELETE = 'DELETE';

    private $timeout = 0;
    private $header = array();
    private $responseHeaders = array();
    private static $instance = null;
    private $errno = 0;
    private $errstr = '';
    private $url = '';
    private $autoHost = true;
    private $cookie = array();
    private $isProxy = false;
    private $ch = null;
    private $followLocation = true;
    protected $statusCode;
    protected $statusText;

    /**
     *
     * @param int $timeout
     */
    public function __construct($timeout = 10)
    {
        $this->setTimeout($timeout);
        $this->ch = curl_init();
        curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($this->ch, CURLOPT_HEADER, true);
        curl_setopt($this->ch, CURLINFO_HEADER_OUT, true);
        curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, false);
    }

    public function __destruct()
    {
        curl_close($this->ch);
    }

    /**
     *
     * @param int $timeout
     * @return HTTPClient
     */
    public static function getInstance($timeout = 10)
    {
        if (is_null(self::$instance)) {
            self::$instance = new self($timeout);
        }
        return self::$instance;
    }

    /**
     * @param $cert
     * @param $certkey
     * @param $certpwd
     * @param null $rootca
     */
    public function setSSLOpt($cert, $certkey, $certpwd, $rootca = null)
    {
        curl_setopt($this->ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($this->ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($this->ch, CURLOPT_SSLCERT, $cert);
        curl_setopt($this->ch, CURLOPT_SSLCERTPASSWD, $certpwd);
        curl_setopt($this->ch, CURLOPT_SSLKEY, $certkey);

        if (!is_null($rootca)) {
            curl_setopt($this->ch, CURLOPT_CAINFO, $rootca);
        }
    }

    /**
     *
     * @param string $name
     * @param string $value
     */
    public function setOneHeader($name, $value)
    {
        $this->header[$name] = $value;
    }

    /**
     *
     * @param string $name
     * @param string $value
     */
    public function setOneCookie($name, $value)
    {
        $this->cookie[$name] = $value;
    }

    /**
     *
     * @param boolean $flag
     */
    public function setFollowLocation($flag)
    {
        $this->followLocation = $flag;
    }

    /**
     *
     * @param boolean $flag
     */
    public function setAutoHost($flag)
    {
        $this->autoHost = $flag ? true : false;
    }

    /**
     *
     * @return array
     */
    public function getCookie()
    {
        return $this->cookie;
    }

    /**
     * @param null $contentType
     */
    public function setContentType($contentType = null)
    {
        if (is_null($contentType)) {
            unset($this->header['Content-type']);
            return;
        }
        $this->header['Content-type'] = $contentType;
    }

    /**
     * cookie
     */
    private function buildCookie()
    {
        $arr = array();
        foreach ($this->cookie as $name => $value) {
            $arr[] = $name . '=' . $value;
        }
        $this->setOneHeader('Cookie', implode(';', $arr));
    }

    /**
     * header
     */
    public function buildHeader()
    {
        curl_setopt($this->ch, CURLOPT_HTTPHEADER, $this->getRequestHeader());
    }

    /**
     * 保存cookie
     */
    private function saveCookie()
    {
        $cookies = $this->getResponseHeaderItem('Set-Cookie');
        foreach ($cookies as $k => $v) {
            $arr = explode(';', $v);
            $str = $arr[0];

            $pos = strpos($str, '=');
            if ($pos !== false && $pos > 0) {
                $this->setOneCookie(substr($str, 0, $pos), substr($str, $pos + 1));
            }
        }
    }

    /**
     * 获取请求头
     * @return array
     */
    public function getRequestHeader()
    {
        $item = array();
        foreach ($this->header as $k => $v) {
            $item[] = $k . ': ' . $v;
        }
        return $item;
    }

    /**
     *
     * @return int
     */
    public function getStatusCode()
    {
        return $this->statusCode;
    }

    /**
     *
     * @return string
     */
    public function getStatusText()
    {
        return $this->statusText;
    }

    /**
     *
     * @param string $type
     * @param string $content
     * @return resource
     */
    private function createContext($type = '', $content = '')
    {
        $ch = $this->ch;
        curl_setopt($ch, CURLOPT_URL, $this->url);
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
        curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, $this->followLocation);
        if (!$this->isProxy) {
            switch ($type) {
                case self::METHOD_POST:
                case self::METHOD_PUT:
                    //$this->setContentType('application/x-www-form-urlencoded');
                    // $this->setOneHeader('Content-Length', strlen($content));
                    break;
                default :
                    $this->setContentType(null);
            }
            if ($this->autoHost) {
                $this->setHostFromUrl();
            }
            $this->buildCookie();
        }
        curl_setopt($ch, CURLOPT_NOBODY, $type == self::METHOD_HEAD);
        $this->buildHeader();
        if ($content != '' && in_array($type, array(self::METHOD_PUT, self::METHOD_POST))) {
            curl_setopt($this->ch, CURLOPT_POSTFIELDS, $content);
        }
        curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $type);
        return $ch;
    }

    /**
     * 设置HOST
     */
    public function setHostFromUrl()
    {
        $arr = parse_url($this->url);
        if (isset($arr['host'])) {
            $this->setOneHeader('Host', $arr['host']);
        }
    }

    /**
     *
     * @param int $timeout
     */
    public function setTimeout($timeout)
    {
        $this->timeout = $timeout;
    }

    /**
     * @param $url
     * @param array $data
     * @return string
     */
    public function get($url, array $data = array())
    {
        if (!empty($data)) {
            $params = http_build_query($data);
            if (!strpos($url, '?')) {
                $url .= '?';
            }
            $url .= $params;
        }

        $this->url = $url;
        $context = $this->createContext(self::METHOD_GET);
        return $this->request($context);
    }

    /**
     * @param $url
     * @param array $data
     * @return string
     */
    public function post($url, array $data)
    {
        $this->url = $url;
        foreach ($data as $k => $v) {
            if (!is_string($v) || strpos($v, '@') !== 0) {
                $post[$k] = $v;
                continue;
            }
            $file = str_replace('@', '', $v);
            if (!is_file($file)) {
                echo "文件{$file}不在存";exit();
            }
            $post[$k] = curl_file_create($file, $this->getMime($file));
        }
        $context = $this->createContext(self::METHOD_POST, $post);
        return $this->request($context);
    }

    /**
     * @param $url
     * @param $vars
     * @param array $pem
     * @param $key
     * @param int $second
     * @param array $aHeader
     * @return bool|mixed|string
     */
    public function post_ssl($url, $vars, Array $pem, $key, $second = 30, $aHeader = array("content-type: application/x-www-form-urlencoded; 
charset=UTF-8"))
    {
        if (!$key) {
            return 'no key';
        }
        $cert = $pem['cert'];
        $cert_key = $pem['cert_key'];
        $rootca = $pem['rootca'];
        $ch = curl_init();
        //curl_setopt($ch,CURLOPT_VERBOSE,'1');
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLCERT, $cert);
        curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $key);
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, $cert_key);
        curl_setopt($ch, CURLOPT_CAINFO, $rootca);
        if (count($aHeader) >= 1) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
        }

        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);
        $data = curl_exec($ch);

        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            echo "call faild, errorCode:$error\n";
            curl_close($ch);
            return false;
        }
    }

    /**
     * 获取文件mime
     * @param string $file
     * @return string
     */
    protected function getMime($file)
    {
        if (!is_file($file)) {
            return '';
        }
        $finfo = new \finfo(FILEINFO_MIME);
        $mime = $finfo->file($file);
        return $mime;
    }

    /**
     * @param $url
     * @param $text
     * @return string
     */
    public function postText($url, $text)
    {
        $this->url = $url;
        $context = $this->createContext(self::METHOD_POST, $text);
        return $this->request($context);
    }

    /**
     * @param $url
     * @return string
     */
    public function head($url)
    {
        $this->url = $url;
        $context = $this->createContext(self::METHOD_HEAD);
        return $this->request($context);
    }

    /**
     *
     * @return array
     */
    public function getResponseHeader()
    {
        return $this->responseHeaders;
    }

    /**
     *
     * @param string $name
     * @return array
     */
    public function getResponseHeaderItem($name)
    {
        $headers = $this->getResponseHeader();
        $res = array();
        foreach ($headers as $item) {
            $pos = strpos($item, ':');
            if ($pos !== false && $pos > 0 && substr($item, 0, $pos) == $name) {
                $res[] = trim(substr($item, $pos + 1));
            }
        }
        return $res;
    }

    /**
     *
     * @param resource $context
     * @return string
     */
    private function request($context)
    {
        $this->setError(0, '');

        $res = curl_exec($context);
        if ($res === false) {
            $error = curl_error($context);
            var_dump($error);
            die();
        }
        $headerSize = curl_getinfo($context, CURLINFO_HEADER_SIZE);
        $header = substr($res, 0, $headerSize - 4);

        if ($res === false) {
            $error = error_get_last();
            if (empty($error)) {
                $error = array(
                    'type' => 'curl_error',
                    'message' => curl_strerror(curl_errno($this->ch))
                );
            }
            $this->setError($error['type'], $error['message']);

            $this->responseHeaders = array();
            return false;
        }
        $this->setError(0, '');
        $this->responseHeaders = explode("\r\n", $header);

        $this->parseStatusInfo();
        $this->saveCookie();
        return substr($res, $headerSize);
    }

    /**
     * 解析状态码和描述
     */
    private function parseStatusInfo()
    {
        if ($this->getErrorCode() != 0) {
            $this->statusCode = 0;
            $this->statusText = '';
            return;
        }
        $info = $this->getResponseHeader();
        $status = trim(strstr($info[0], ' '));
        $pos = strpos($status, ' ');
        $this->statusCode = intval(substr($status, 0, $pos));
        $this->statusText = trim(substr($status, $pos));
    }

    /**
     *
     * @param int $errno
     * @param string $errstr
     */
    private function setError($errno = 0, $errstr = '')
    {
        $this->errno = $errno;
        $this->errstr = $errstr;
    }

    /**
     *
     * @return int
     */
    public function getErrorCode()
    {
        return $this->errno;
    }

    /**
     *
     * @return string
     */
    public function getErrorMessage()
    {
        return $this->errstr;
    }

    /**
     * 获取客户端请求头
     * @return array
     */
    public function getAllClientheaders()
    {
        $h = array('CONTENT_LENGTH', 'CONTENT_TYPE');
        $headers = array();
        foreach ($_SERVER as $name => $value) {
            if (in_array($name, $h)) {
                $headers[str_replace('_', '-', ucwords(strtolower($name)))] = $value;
                continue;
            }
            if (substr($name, 0, 5) == 'HTTP_') {
                $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
            }
        }
        return $headers;
    }

    public function getFileNameFromDisposition()
    {
        $value = $this->getResponseHeaderItem('Content-disposition');
        if (empty($value)) {
            return false;
        }
        $disposition = $value[0];

        $pos = strpos($disposition, ';');
        $fileinfo = substr($disposition, $pos + 1);

        $pos2 = strpos($fileinfo, '=');
        $file = substr($fileinfo, $pos2 + 1);

        if (strpos($file, '"') === 0) {
            $file = substr($file, 1);
        }
        $len = strlen($file);
        if (strpos($file, '"') === $len - 1) {
            $file = substr($file, 0, $len - 1);
        }
        return $file;
    }

    /**
     * 代理
     * @param string $url
     * @param string $cookieString
     * @return string
     */
    public function proxy($url, $cookieString = '')
    {
        $this->url = $url;
        $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';

        $this->isProxy = true;
        $headers = $this->getAllClientheaders();
        if ($cookieString != '') {
            $headers['Cookie'] = $cookieString;
        }
        $urlInfo = parse_url($url);
        $headers['Referer'] = $urlInfo['scheme'] . '://' . $urlInfo['host'];
        $this->header = $headers;
        $this->setHostFromUrl();

        switch ($method) {
            case 'POST':
            case 'PUT':
                $data = file_get_contents('php://input');
                $context = $this->createContext($method, $data);
                break;
            default :
                $context = $this->createContext($method);
        }
        $res = $this->request($context);

        $resHeaders = $this->getResponseHeader();
        foreach ($resHeaders as $item) {
            header($item);
        }
        return $res;
    }

    /**
     * @param array $nodes
     * @param $saveDir
     * @param int $timeout
     */
    public static function multiDownload(array $nodes, $saveDir, $timeout = 10)
    {
        if (empty($nodes)) {
            return;
        }
        if (!is_dir($saveDir)) {
            mkdir($saveDir);
        }
        $mh = curl_multi_init();
        $curlArray = array();
        foreach ($nodes as $item) {
            $ch = curl_init();
            $curlArray[] = array(
                'name' => $item['name'],
                'ch' => $ch
            );
            curl_setopt($ch, CURLOPT_URL, $item['url']);
            curl_setopt($ch, CURLOPT_HEADER, false);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
            curl_multi_add_handle($mh, $ch);
        }
        $active = null;
        do {
            $mrc = curl_multi_exec($mh, $active);
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);

        while ($active && $mrc == CURLM_OK) {
            if (curl_multi_select($mh) != -1) {
                do {
                    $mrc = curl_multi_exec($mh, $active);
                } while ($mrc == CURLM_CALL_MULTI_PERFORM);
            }
        }
        foreach ($curlArray as $item) {
            $path = $item['name'];
            file_put_contents($saveDir . '/' . $path, curl_multi_getcontent($item['ch']));
            curl_multi_remove_handle($mh, $item['ch']);
            curl_close($item['ch']);
        }
        curl_multi_close($mh);
    }

    public function sendPost($url, $data = '')
    {
        if (empty($data)) {
            $headers[] = "Content-Type:application/x-www-form-urlencoded";
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_HEADER, 0);
            $output = curl_exec($ch);
            if ($output === false) {
                $error = curl_error($ch);
                var_dump($error);
                die();
            }
            curl_close($ch);
            return $output;
        } else {
            $headers = array();
            $headers[] = 'Content-Type:application/x-www-form-urlencoded';
            $ch = curl_init();//打开
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
            $response = curl_exec($ch);
            if ($response === false) {
                $error = curl_error($ch);
                var_dump($error);
                die();
            }
            curl_close($ch);//关闭

            return $response;
        }
    }

    /**
     * @param $url
     * @param string $data
     * @param string $accesstoken
     * @param array $header
     * @return bool|string
     */
    public function sendRequert($url, $data = '', $accesstoken = '', $header = [])
    {
        if (empty($data)) {
            if (empty($header)) {
                $headers = array();
                $headers[] = "Content-Type:application/json;";
                if ($accesstoken) {
                    $headers[] = 'Authorization: ' . $accesstoken;
                }
            } else {
                $headers = $header;
            }
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_HEADER, 0);
            $output = curl_exec($ch);
            curl_close($ch);
            return $output;
        } else {
            if (empty($header)) {
                $headers = array();
                $headers[] = "Content-Type:application/json;";
                if ($accesstoken) {
                    $headers[] = 'Authorization: ' . $accesstoken;
                }
            } else {
                $headers = $header;
            }
            $ch = curl_init();//打开
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
            $response = curl_exec($ch);

            if ($response === false) {
                $error = curl_error($ch);
                var_dump($error);
                die();
            }
            curl_close($ch);//关闭

            return $response;
        }
    }

}
